﻿using System;
using System.Collections.Generic;
using System.Linq;
using Didaku.QEngine.Common.Counts;
using Didaku.QEngine.Common.Entities;
using Didaku.QEngine.Common.Enums;
using Didaku.QEngine.Common.Extensions;
using Didaku.QEngine.Common.Interfaces.Engine;
using Didaku.QEngine.Common.Wrapper.Engine;

namespace Didaku.QEngine
{
    /// <summary>统计人数服务
    /// </summary>
    internal class CountService : ICountService
    {
        /// <summary>柜台关系字典：元素与拥有该元素的柜台关系字典
        /// </summary>
        private readonly Dictionary<string, List<string>> _CounterRelations = new Dictionary<string, List<string>>();

        /// <summary>柜台字典：柜台拥有的队列
        /// </summary>
        private readonly Dictionary<string, IEnumerable<string>> _Counters = new Dictionary<string, IEnumerable<string>>();

        /// <summary>队列关系字典：元素与拥有该元素的队列关系字典
        /// </summary>
        private readonly Dictionary<string, List<string>> _ElementRelations = new Dictionary<string, List<string>>();

        private bool _IsRunning;

        public CountService(IDictionary<string, ServiceLogic> logics, IDictionary<string, ServiceQueue> queues)
        {
            InitializeCounters(logics, queues);
            InitializeQueues(queues);
            InitializeElements(queues);
            foreach (ServiceQueue queue in queues.Values)
            {
                queue.TransactionAddedEvent += QueueChangedEvent;
                queue.TransactionChangedEvent += QueueChangedEvent;
            }
        }

        #region ICountService Members

        /// <summary>该引擎对应的柜台等待人数的集合
        /// </summary>
        public CountMap Counters { get; private set; }

        /// <summary>该引擎对应的所有队列等待人数的集合
        /// </summary>
        public CountMap Queues { get; private set; }

        /// <summary>该引擎对应的所有队列元素的集合
        /// </summary>
        public CountMap Elements { get; private set; }

        /// <summary>启动服务
        /// </summary>
        public void Start(IEnumerable<IServiceQueue<Transaction>> queues)
        {
            _IsRunning = true;
            foreach (var queue in queues)
                QueueChangedEvent(queue, null);
        }

        /// <summary>关闭服务
        /// </summary>
        public void Close()
        {
            _IsRunning = false;
        }

        #endregion

        private void InitializeCounters(IDictionary<string, ServiceLogic> logics, IDictionary<string, ServiceQueue> queues)
        {
            Counters = new CountMap();
            foreach (var counter in logics)
            {
                Counters.Add(counter.Key, new Count());
                _Counters.Add(counter.Key, counter.Value.GetQueueIdList());
            }
            //维护队列与柜台的反向关系字典
            foreach (ServiceQueue queue in queues.Values)
            {
                List<string> relation = GetCounterIdRelation(logics.Values, queue.Id.ToString());
                relation.TrimExcess();
                _CounterRelations.Add(queue.Id, relation);
            }
        }

        private void InitializeQueues(IDictionary<string, ServiceQueue> queues)
        {
            Queues = new CountMap();
            foreach (var key in queues.Keys)
            {
                if (!Queues.ContainsKey(key))
                    Queues.Add(key, new Count());
            }
        }

        private void InitializeElements(IDictionary<string, ServiceQueue> queues)
        {
            Elements = new CountMap();
            foreach (ServiceQueue queue in queues.Values)
            {
                foreach (IServiceQueueElement element in queue.Elements)
                {
                    var id = element.GetId();
                    if (!Elements.ContainsKey(id))
                        Elements.Add(id, new Count());
                }
            }
            //维护元素与队列的反向关系字典
            foreach (ServiceQueue queue in queues.Values) //遍历队列
            {
                foreach (IServiceQueueElement element in queue.Elements) //遍历队列中的每一个元素
                {
                    var id = element.GetId();
                    if (!_ElementRelations.ContainsKey(id)) //元素关系表中已有该元素
                    {
                        var relation = GetElementIdRelation(queues.Values, id);
                        relation.TrimExcess();
                        _ElementRelations.Add(id, relation);
                    }
                }
            }
        }

        /// <summary>从队列集合中查找包含指定元素ID的队列的集合
        /// </summary>
        /// <param name="queueArray">The queue array.</param>
        /// <param name="id">The id.</param>
        /// <returns></returns>
        private static List<string> GetElementIdRelation(IEnumerable<ServiceQueue> queueArray, string id)
        {
            return (from queue in queueArray where queue.Elements.Any(ele => id.Equals(ele.GetId())) select queue.Id).ToList();
        }

        /// <summary>从柜台集合中查找包含指定队列ID的柜台的集合
        /// </summary>
        private List<string> GetCounterIdRelation(IEnumerable<ServiceLogic> counterArray, string id)
        {
            var list = new List<string>();
            foreach (ServiceLogic counter in counterArray)
            {
                if (counter.ContainsQueue(id))
                {
                    list.Add(counter.Counter.Id);
                    break;
                }
            }
            return list;
        }

        protected virtual void QueueChangedEvent(IServiceQueue<Transaction> queue, EventArgs e)
        {
            if (!_IsRunning)
                return;
            //1.更新队列的等待人数
            Queues[queue.Id] = queue.Calculate(TransactionStatus.OnWait);
            //2.更新柜台的等待人数
            var counters = _CounterRelations[queue.Id];
            foreach (var counter in counters)
            {
                Count counterCount = SumCounter(counter);
                Counters[counter] = counterCount;
            }
            //3.更新元素的等待人数
            foreach (IServiceQueueElement element in queue.Elements)
            {
                var id = element.GetId();
                Elements[id] = SumElement(id);
            }
        }

        private Count SumElement(string elementId)
        {
            var count = new Count();
            return _ElementRelations[elementId].Aggregate(count, (current, queue) => current + Queues[queue]);
        }

        private Count SumCounter(string counter)
        {
            var count = new Count();
            return _Counters[counter].Aggregate(count, (current, queue) => current + Queues[queue]);
        }
    }
}